﻿using CSRedis;
using Newtonsoft.Json.Linq;
using PuppeteerSharp;
using Q.Spider.Common;
using System.Collections.Concurrent;
using System.Reflection;

namespace Q.Spider.Core
{
    public class QSpiderCore : IDisposable
    {
        /// <summary>
        /// 核心状态
        /// </summary>
        public Models.CoreState State { get; private set; }
        /// <summary>
        /// 核心状态信息描述
        /// </summary>
        public string Message { get; private set; } = string.Empty;

        /// <summary>
        /// 配置信息
        /// </summary>
        public Models.Config Config { get; }
        public CSRedisClient? RedisClient { get; private set; }

        public Engine Engine { get; set; }


        public Action? OnStopComplete { get; set; }

        /// <summary>
        /// core状态信息 用户控制台输出查看
        /// </summary>
        private readonly Models.CoreInfo _CoreInfo = new();

        /// <summary>
        /// 请求队列
        /// </summary>
        readonly ConcurrentQueue<Models.Request> RequestQueue = new();

        /// <summary>
        /// 流
        /// </summary>
        readonly List<FlowBase> Flows = new();

        /// <summary>
        /// 连续错误数
        /// </summary>
        private int ContinuousErrorCount = 0;

        public QSpiderCore(Models.Config config)
        {
            //是否有配置文件 如果有了 就继续用 没有new一个
            Config = config ?? new Models.Config();
            //看看是否需要开启分布式爬虫  如果有配置信息 就去 new redis客户端
            if (!string.IsNullOrWhiteSpace(Config.RedisConnStr))
            {
                if (config!.RedisDatabase > 0)
                {
                    config.RedisConnStr = config!.RedisConnStr!.Replace("defaultDatabase=0", "defaultDatabase=" + config.RedisDatabase);
                }
                RedisClient = new CSRedisClient(config.RedisConnStr);
            }
            Engine = new Engine(this);


            //向中心服务器注册

        }




        /// <summary>
        ///  请求添加
        /// </summary>
        public void AddRequest(Models.Request request)
        {
            //如果 开启了 redis客户端 那么就走redis队列
            if (RedisClient != null)
            {
                RedisClient.SAdd("QSpiderRequestQueue", request);
                return;
            }
            //否则走普通的队列
            RequestQueue.Enqueue(request);
        }

        /// <summary>
        /// 添加处理流
        /// </summary>
        /// <param name="flowBase"></param>
        public void AddFlow(FlowBase flowBase) => Flows.Add(flowBase);


        /// <summary>
        ///重试错误请求
        /// </summary>
        public void RetryErrorRequest()
        {

            if (RedisClient != null)
            {
                try
                {
                    var all = RedisClient.LRange<JObject>("ErrRequest", 0, -1);
                    foreach (var item in all)
                    {
                        var json = item["Request"];
                        var req = new Models.Request(json["Uri"].Value<string>())
                        {
                            Depth = json["Depth"].Value<int>(),
                            ErrorCount = 0
                        };
                        foreach (var p in (JObject)json["Properties"])
                        {
                            req.AddProperties(p.Key, (JObject)p.Value);
                        }
                        this.AddRequest(req);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("错误" + ex.Message);
                }
            }
        }


        /// <summary>
        /// 获取core状态信息
        /// </summary>
        public Models.CoreInfo GetInfo()
        {
            // 获取请求队列的数量 看看从哪个里面拿
            if (RedisClient != null)
            {
                _CoreInfo.RequestQueueCount = RedisClient.SCard("QSpiderRequestQueue");
            }
            else
            {
                _CoreInfo.RequestQueueCount = RequestQueue.Count;
            }
            _CoreInfo.State = State;
            _CoreInfo.Message = Message;

            return _CoreInfo;

        }

        /// <summary>
        ///  无头有头模式切换
        /// </summary>
        public void ChangeEngineHeadlessHead(bool isHide)
        {
            if (Config.EngineIsHeadless == isHide)
            {
                return;
            }
            Config.EngineIsHeadless = isHide;
            if (State == Models.CoreState.运行中)
            {
                State = Models.CoreState.重启浏览器中;
            }
        }


        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            if (State != Models.CoreState.运行中)
            {
                LogHelper.Logger.Warn("未运行中状态，无法停止，稍后再试");
                return;
            }
            State = Models.CoreState.停止中;
        }

        public void Start()
        {
            if (State == Models.CoreState.运行中)
            {
                return;
            }

            if (State != Models.CoreState.待运行)
            {
                Message = State.ToString() + "状态不能开启，请稍后再试";
                LogHelper.Logger.Warn(Message);
                return;
            }

            Engine?.Dispose();
            Engine = new Engine(this);



            State = Models.CoreState.运行中;
            Message = "";
            _ = Task.Run(async () =>
            {

                while (true)
                {
                    //执行指令
                    if (State == Models.CoreState.重启浏览器中)
                    {
                        Engine?.Dispose();
                        Engine = new Engine(this);
                        State = Models.CoreState.运行中;
                    }





                    Models.FlowContext context = null;
                    Models.Request request = null;

                    try
                    {
                        request = GetNextRequest();
                        //如果请求队列空了
                        if (request == null)
                        {
                            await Task.Delay(100);
                            continue;
                        }
                        context = await RunRequestAsync(request);
                    }
                    catch (NextRequestException nrex)
                    {
                        LogHelper.Logger.Error(nrex, "请求队列错误");
                        continue;
                    }
                    catch (LimitException lex)
                    {
                        _CoreInfo.ErrorCount++;
                        ContinuousErrorCount++;
                        LogHelper.Logger.Error(lex, "HandleLimitAsync");
                        await HandleLimitAsync();
                    }
                    catch (Exception ex)
                    {
                        _CoreInfo.ErrorCount++;
                        ContinuousErrorCount++;
                        LogHelper.Logger.Error(ex, "未处理错误");
                    }

                    if (IsStop())
                    {
                        OnStopComplete?.Invoke();
                        break;
                    }
                }
            });
        }



        private async Task HandleLimitAsync()
        {
            try
            {
                if (Config.GetNewProxyAsync != null)
                {
                    if (State == Models.CoreState.运行中)
                    {
                        State = Models.CoreState.重启浏览器中;
                        Config.Proxy = await Config.GetNewProxyAsync.Invoke(this);
                    }
                }
                else
                {
                    State = Models.CoreState.停止中;
                    Message = "被限制，停止";
                    LogHelper.Logger.Error("引擎被限制了，停止运行");
                }
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error(ex, "处理Limit异常");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private bool IsStop()
        {
            if (State == Models.CoreState.停止中)
            {
                State = Models.CoreState.待运行;
                Message += DateTime.Now + "停止";
                LogHelper.Logger.Info("引擎停止运行");
                return true;
            }

            if (ContinuousErrorCount > 3)
            {
                State = Models.CoreState.待运行;
                Message = "已经连续错误" + ContinuousErrorCount + "个请求，停止运行";
                LogHelper.Logger.Info(Message);
                ContinuousErrorCount = 0;
                return true;
            }


            return false;
        }


        private async Task<Models.FlowContext> RunRequestAsync(Models.Request request)
        {
            var context = new Models.FlowContext(this, request);
            try
            {
                foreach (var item in Flows)
                {
                    //如果制定了流
                    if (request.FlowTpyes != null
                        && !request.FlowTpyes.Contains(item.GetType()))
                    {
                        continue;
                    }


                    //反射生成一个
                    var flowClass = Activator.CreateInstance(item.GetType(), true);
                    var flow = (FlowBase)flowClass;
                    flow.Init(context);
                    await flow.RunAsync(context);
                }
                switch (context.State)
                {
                    case Models.FlowContextHandelState.未处理:
                        AddErrorRequest(context, false, null);
                        break;
                    case Models.FlowContextHandelState.错误:
                        AddErrorRequest(context, true, null);
                        break;
                    case Models.FlowContextHandelState.成功:
                        _CoreInfo.SuccessCount++;
                        break;
                }



            }
            catch (LimitException lex)
            {
                context.IsLimit = true;
                AddErrorRequest(context, false, lex);
                throw;
            }
            catch (NavigationException nex)
            {
                AddErrorRequest(context, false, nex);
                if (nex.Message.Contains("PROXY_CONNECTION_FAILED"))
                {
                    context.IsLimit = true;
                    throw new LimitException("PROXY_CONNECTION_FAILED");
                }
                throw nex;
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error(ex, "解析错误");
                LogHelper.Logger.Error(request.Uri.ToString());
                AddErrorRequest(context, true, ex);
                throw;
            }

            ContinuousErrorCount = 0;
            return context;
        }


        /// <summary>
        /// 添加错误request至队列
        /// </summary>
        /// <param name="context">上下文</param>
        /// <param name="IsAddErrorCount">是否增加请求错误数</param>
        /// <param name="ex"></param>
        private void AddErrorRequest(Models.FlowContext context, bool IsAddErrorCount, Exception ex)
        {
            try
            {
                if (IsAddErrorCount)
                {
                    context.Request.ErrorCount++;
                }

                // 如果请求发生错误 但是没有超过设置的最大值  那么错误次数加1  并且重新添加到请求队列 等待重试执行
                if (context.Request.ErrorCount < Config.RequestMaxRetryCount)
                {
                    AddRequest(context.Request);
                }
                else if (RedisClient != null)
                {
                    //如果 一个请求的 错误并且重试超过 设定值 将会进入 错误队列（并且需要保证开启了redis客户端）
                    RedisClient.LPush("ErrRequest", new
                    {
                        Date = DateTime.Now,
                        Request = context.Request,
                        Error = ex?.Message ?? "无异常",
                        InnerException = ex?.InnerException?.Message ?? "无内部异常"
                    });
                }
            }
            catch (Exception rex)
            {
                LogHelper.Logger.Error(rex, "保存到Redis失败");
            }
        }

        /// <summary>
        ///  获取一条新的请求
        /// </summary>
        private Models.Request GetNextRequest()
        {
            try
            {
                if (RedisClient != null)
                {
                    var json = RedisClient.SPop<JObject>("QSpiderRequestQueue");
                    if (json == null)
                    {
                        return null;
                    }
                    var req = new Models.Request(json["Uri"].Value<string>())
                    {
                        Depth = json["Depth"].Value<int>(),
                        ErrorCount = json["ErrorCount"].Value<int>()
                    };

                    foreach (var item in (JObject)json["Properties"])
                    {
                        req.AddProperties(item.Key, (JObject)item.Value);
                    }
                    return req;
                }
                return RequestQueue.TryDequeue(out var request) ? request : null;
            }
            catch (Exception ex)
            {
                throw new NextRequestException(ex.Message);
            }
        }




        private void RegisterCoreToCenterServer()
        {
            // 获取Ip地址
            //
            //config 
        }




        public void Dispose()
        {
            Engine?.Dispose();
        }
    }
}